#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : Lee
# @File    : dhy_connect_row_view.py
# @Time    : 2024/1/15 10:49
import os
import json
from util.global_config import *
from util.common_calc import *
from PyQt5.QtWidgets import QWidget
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMessageBox
from util.connect_row_calc import GetConnectRowAverPoint
from views.alert_messagebox_view import show_message_box
from views.dhy_connect_row_matplotlib_view import ConnectRowMplWidget
from views.resource_view.connect_row_wigdet import Ui_connect_row_view


class DHYConnectRowView(QWidget, Ui_connect_row_view):
    def __init__(self, parent=None):
        super(DHYConnectRowView, self).__init__(parent)
        self.setupUi(self)

        # 添加matplotlib视图
        matplotlib_widget = ConnectRowMplWidget(self)
        self.matplotlib_layout.addWidget(matplotlib_widget)
        self.matplotlib_view = matplotlib_widget.qCanvas

        # 加载数据按钮监听事件
        self.load_data_btn.clicked.connect(self.select_csv_file)
        self.csv_file_path = ""

        self.test_set_btn.clicked.connect(self.start_connect_row_test)

        self.matplotlib_view_points = list()

        self.point_a_x_text.editingFinished.connect(lambda: self.finish_edit_text("point_a_x"))
        self.point_a_y_text.editingFinished.connect(lambda: self.finish_edit_text("point_a_y"))
        self.point_b_x_text.editingFinished.connect(lambda: self.finish_edit_text("point_b_x"))
        self.point_b_y_text.editingFinished.connect(lambda: self.finish_edit_text("point_b_y"))

    @QtCore.pyqtSlot()
    def select_csv_file(self):
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog
        self.csv_file_path, _ = QtWidgets.QFileDialog.getOpenFileName(None, "QFileDialog.getOpenFileName()", "",
                                                                      "CSV Files (*.csv)", options=options)
        self.load_data_from_csv()

    @QtCore.pyqtSlot()
    def start_connect_row_test(self):
        _left_x_values = self.matplotlib_view.left_line.get_xdata()
        _left_y_values = self.matplotlib_view.left_line.get_ydata()
        _right_x_values = self.matplotlib_view.right_line.get_xdata()
        _right_y_values = self.matplotlib_view.right_line.get_ydata()

        if not _left_x_values or not _left_y_values or not _right_x_values or not _right_y_values:
            return

        if not self.dis_text.text():
            QMessageBox.information(self, "提示", "请输入衔接行间距！")
            return

        a_left, b_left, c_left = calc_baseline_equation([_left_x_values[0], _left_y_values[0]],
                                                        [_left_x_values[1], _left_y_values[1]])
        a_right, b_right, c_right = calc_baseline_equation([_right_x_values[0], _right_y_values[0]],
                                                           [_right_x_values[1], _right_y_values[1]])
        # 第一步：获取两个平行线之间的定位点
        _lines_dict = {"line_index_0": []}
        _line_index = 0
        _last_point_index = 0
        _position_array = list()
        for point_index, point in enumerate(self.matplotlib_view_points):
            _point_location = [
                get_point_location_of_line(point, a_left, b_left, c_left),
                get_point_location_of_line(point, a_right, b_right, c_right)
            ]
            if "up" in _point_location and "down" in _point_location:
                _position_array.append(point)
                # 同一行两个截断线之间的定位索引值是连续的
                # _last_point_index 记录上一个循环的索引值
                if _last_point_index == 0:
                    _last_point_index = point_index - 1

                # 轨迹索引不连续时(定位点可能在直线左右波动，设置100为波动宽幅)，认为当前车辆换行作业， 作业索引值 + 1
                if point_index - _last_point_index > 100:
                    _line_index = _line_index + 1

                _cur_line_index = f"line_index_{_line_index}"
                # 当前行没有被记录，则创建一个新的key值，保存当前行轨迹数据
                if _cur_line_index not in _lines_dict.keys():
                    _lines_dict[_cur_line_index] = []

                _lines_dict[_cur_line_index].append(list(point))

                _last_point_index = point_index

        # self.matplotlib_view.update_data(_position_array)

        # 第二步：将平行线间的点以x轴数值大小升序排列, 将行驶轨迹以从下到上的顺序排列
        _line_point_y = dict()
        for line, points in _lines_dict.items():
            if points[0][0] > points[-1][0]:
                points.reverse()
            _line_point_y[str(points[0][1])] = line
            _lines_dict[line] = points
        _line_sequence = [_line_point_y[point_y] for point_y in sorted(_line_point_y.keys())]

        # 第三步：分别以均分基准线/数值/轨迹长度的方式获取需要计算的点(ab点从matplotlib_view中获取)
        _x_points = self.matplotlib_view.ab_line.get_xdata()
        _y_points = self.matplotlib_view.ab_line.get_ydata()
        _point_a = [_x_points[0], _y_points[0]]
        _point_b = [_x_points[1], _y_points[1]]

        _aver_line_dict = dict()
        for sorted_line in _line_sequence:
            _c_r_obj = GetConnectRowAverPoint(_lines_dict[sorted_line], _point_a, _point_b)
            _aver_line, _aver_count, _aver_trajectory = _c_r_obj.get_connect_row_aver_point()
            _aver_line_dict[sorted_line] = {
                "aver_line": _aver_line,
                "aver_count": _aver_count,
                "aver_trajectory": _aver_trajectory
            }

        # 第四步：计算衔接行精度值、平均误差
        # 获取衔接行距离

        _aver_line_dis_list = list()
        _aver_count_dis_list = list()
        _aver_trajectory_dis_list = list()
        _aver_line_variance_list = list()
        _aver_count_variance_list = list()
        _aver_trajectory_variance_list = list()

        _dis_set_value = float(self.dis_text.text())

        for _sequence_index in range(0, len(_line_sequence) - 1):
            _dis_dict = dict()
            # 当前行均分点数值
            _cur_line_aver_data = _aver_line_dict[_line_sequence[_sequence_index]]
            # 下一行均分点数值
            _next_line_aver_data = _aver_line_dict[_line_sequence[_sequence_index + 1]]

            for aver_type in ["aver_line", "aver_count", "aver_trajectory"]:
                for _index in range(len(_cur_line_aver_data[aver_type])):
                    if len(_next_line_aver_data[aver_type]) > _index:
                        _cur_point = _cur_line_aver_data[aver_type][_index]  # 当前行轨迹点
                        _next_point = _next_line_aver_data[aver_type][_index]  # 下一行轨迹点
                        _dis = get_distance_of_two_point(_cur_point, _next_point)  # 两点距离
                        if aver_type not in _dis_dict.keys():
                            _dis_dict[aver_type] = [_dis]
                        else:
                            _dis_dict[aver_type].append(_dis)
                    else:
                        break
        # 计算平均误差
            _aver_line_dis_list.append(sum([abs(_dis - _dis_set_value) for
                                            _dis in _dis_dict["aver_line"]]) / len(_dis_dict["aver_line"]) * 100)
            _aver_count_dis_list.append(sum([abs(_dis - _dis_set_value) for
                                             _dis in _dis_dict["aver_count"]]) / len(_dis_dict["aver_count"]) * 100)
            _aver_trajectory_dis_list.append(sum([abs(_dis - _dis_set_value) for
                                                  _dis in _dis_dict["aver_trajectory"]]) /
                                             len(_dis_dict["aver_trajectory"]) * 100)
            # 计算衔接行精度
            _aver_line_variance_list.append(get_connect_row_variance(_dis_dict["aver_line"], _dis_set_value))
            _aver_count_variance_list.append(get_connect_row_variance(_dis_dict["aver_count"], _dis_set_value))
            _aver_trajectory_variance_list.append(get_connect_row_variance(_dis_dict["aver_trajectory"],
                                                                           _dis_set_value))
        _format_message = f"""衔接行平均误差(单位：cm)：\n
        均分轨迹点：{round(max(_aver_count_dis_list), 2)}\n
        均分轨迹长度：{round(max(_aver_trajectory_dis_list), 2)}\n
        均分轨迹在基准线上的投影：{round(max(_aver_line_dis_list), 2)}\n\n衔接行精度(单位：cm)：\n
        均分轨迹点：{round(max(_aver_count_variance_list), 2)}\n
        均分轨迹长度：{round(max(_aver_trajectory_variance_list), 2)}\n
        均分轨迹在基准线上的投影：{round(max(_aver_line_variance_list), 2)}"""

        _title = "测试结果"
        _btn_text_list = ["导出", "确定"]

        clicked_btn = show_message_box(_title, _format_message, _btn_text_list)
        if clicked_btn == "导出":
            # 创建一个对话框让用户选择导出路径
            directory = QtWidgets.QFileDialog. \
                getExistingDirectory(None, "选择导出目录", "",
                                     QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks)

            with open(os.path.join(directory, os.path.basename(self.csv_file_path)[:-4] + "_res.json"), 'w',
                      encoding='utf-8') as f_obj:
                json.dump({
                    "衔接行间距(单位：m)": self.dis_text.text(),
                    "衔接行平均误差(单位：cm)": {
                        "均分轨迹点": round(max(_aver_count_dis_list), 2),
                        "均分轨迹长度": round(max(_aver_trajectory_dis_list), 2),
                        "均分轨迹在基准线上的投影": round(max(_aver_line_dis_list), 2)
                    },
                    "衔接行精度(单位：cm)": {
                        "均分轨迹点": round(max(_aver_count_variance_list), 2),
                        "均分轨迹长度": round(max(_aver_trajectory_variance_list), 2),
                        "均分轨迹在基准线上的投影": round(max(_aver_line_variance_list), 2)
                    }
                }, f_obj, ensure_ascii=False)

    def load_data_from_csv(self):
        self.matplotlib_view_points = self.matplotlib_view.load_data_with_csv_file_path(self.csv_file_path)
        if self.matplotlib_view_points:
            self.point_a_x_text.setText(a_x)
            self.point_a_y_text.setText(a_y)
            self.point_b_x_text.setText(b_x)
            self.point_b_y_text.setText(b_y)
            self.matplotlib_view.update_line([[float(a_x), float(b_x)],
                                              [float(a_y), float(b_y)]], "ab_line")

    def finish_edit_text(self, text_type):
        if not eval(f"self.{text_type}_text.text()"):
            eval(f"self.{text_type}_text.setText('0')")
        # 更新AB线
        x_points = [float(self.point_a_x_text.text()), float(self.point_b_x_text.text())]
        y_points = [float(self.point_a_y_text.text()), float(self.point_b_y_text.text())]

        if not x_points or 0.0 in x_points or not y_points or 0.0 in y_points:
            pass
        else:
            self.matplotlib_view.update_line([x_points, y_points], "ab_line")

    def get_clicked_index(self, clicked_index):
        x_points = self.matplotlib_view.ab_line.get_xdata()
        y_points = self.matplotlib_view.ab_line.get_ydata()

        point = self.matplotlib_view_points[clicked_index]

        _title = "设置左、右截断线"
        _text = "左右截断线全部设置后，将截取两条截断线之前的轨迹点参与衔接行精度计算"
        _btn_text_list = ["左截断线", "右截断线", "取消"]

        if not x_points or 0.0 in x_points or not y_points or 0.0 in y_points:
            _title = "设置A、B点坐标"
            _text = "A、B点坐标全部设置后，点击定位点将设置左右截断线"
            _btn_text_list = ["设为A点", "设为B点", "取消"]

        clicked_btn = show_message_box(_title, _text, _btn_text_list)

        if "截断线" in clicked_btn:
            a_points = [float(self.point_a_x_text.text()), float(self.point_a_y_text.text())]
            b_points = [float(self.point_b_x_text.text()), float(self.point_b_y_text.text())]

            a, b, c = calc_baseline_equation(a_points, b_points)
            # 经过point 垂直于AB线的直线方程为: y = b / a * x + k
            k = point[1] - b * point[0] / a

            matplotlib_view_y_values = self.matplotlib_view.draw_point.get_ydata()
            _y_max = max(matplotlib_view_y_values)
            _y_min = min(matplotlib_view_y_values)

            _up_points = [a / b * _y_max - a / b * k, _y_max]
            _down_points = [a / b * _y_min - a / b * k, _y_min]

            _up_x_points = [_up_points[0], _down_points[0]]
            _down_y_points = [_up_points[1], _down_points[1]]
            self.matplotlib_view.update_line([_up_x_points, _down_y_points],
                                             "left_line" if clicked_btn == "左截断线" else "right_line")
        elif "设为A点" == clicked_btn:
            self.point_a_x_text.setText(str(point[0]))
            self.point_a_y_text.setText(str(point[1]))
            self.finish_edit_text("point_a_x")
        elif "设为B点" == clicked_btn:
            self.point_b_x_text.setText(str(point[0]))
            self.point_b_y_text.setText(str(point[1]))
            self.finish_edit_text("point_b_x")
        else:
            print("点击了取消按钮。。。")
